﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;

namespace Nuts
{
    namespace Birds
    {
        struct DynamicsParams
        {
            float flap_delay;
            float flap_impulse;

            float flap_threshold_y;

            float lateral_damping;
            float lateral_max_acceleration;
            float lateral_max_speed;

            float upward_max_speed;
        };

        class BirdTest
        {
            public const int WIDTH = 58;
            public const int HEIGHT = 70;

            public readonly float PERCHED_BASE_DURATION = 2.0f;
            
            public readonly float FLAP_DELAY_RELAX = 0.4f;
            public readonly float FLAP_THRESHOLD_Y = 20.0f;            
            public readonly float FLAP_IMPULSE_BASE = 180.0f;

            public readonly float LATERAL_DAMPING = 1.0f;

            public readonly float MAX_LATERAL_ACCELERATION = 400.0f;
            public readonly float MAX_LATERAL_VELOCITY = 200.0f;
            public readonly float MAX_UPWARD_VELOCITY = 120.0f;

            public readonly Vector2 GRAVITY_FLYING = new Vector2(0.0f, 200.0f);
            public readonly Vector2 GRAVITY = new Vector2(0.0f, 600.0f);

            public readonly float STUN_DURATION = 0.3f;
            public readonly int   MAX_HIT_POINTS = 3;

            public readonly float BIRD_STUN_KICK =   50.0f;
            public readonly float BIRD_DOWN_KICK = -250.0f;

            readonly Rectangle  _rectOwlSpriteAwake = new Rectangle(200, 0, 58, 70);
            static Texture2D    _texBirds;

            public BirdTest(Game1 game)
            {
                _game = game;
            }

            public void Initialize()
            {
                _position.X = _game.RandomFloat(20.0f, Game1.SCREEN_WIDTH-20.0f);
                _position.Y = _game.RandomFloat(Game1.SCREEN_HEIGHT / 2, 20.0f);

                _velocity = Vector2.Zero;

                _max_lateral_velocity = _game.RandomFloat(0.80f, 1.2f) * MAX_LATERAL_VELOCITY;

                _fly_state = FlyState.Perched;
                _draw_in_background = false;
                _timer = PERCHED_BASE_DURATION;
                _hit_points = MAX_HIT_POINTS;

                UpdateHitBox();
            }

            public static void LoadContent(Game1 game)
            {
                _texBirds = game.Content.Load<Texture2D>("go_nuts_owl_00");
            }

            public void Update(GameTime gametime)
            {
                float dt = (float)gametime.ElapsedGameTime.TotalSeconds;

                switch(_fly_state)
                {
                    case FlyState.Perched:
                        // wait for next move
                        _timer -= dt;
                        if(_timer < 0.0f)
                        {
                            // plan next move
                            SelectRandomTarget();
                            TakeOff();
                        }
                        break;

                    case FlyState.Flying:
                        // apply gravity
                        _velocity += GRAVITY_FLYING * dt;

                        // where is my target?
                        float delta_x = _target_pos.X - _position.X;
                        float delta_y = _position.Y - _target_pos.Y; // Y down...

                        // accelerate toward target in X
                        float accel = delta_x - _velocity.X * LATERAL_DAMPING;
                        float vel_step = Math.Sign(accel) * Math.Min(Math.Abs(accel), MAX_LATERAL_ACCELERATION) * dt;
                        _velocity.X += vel_step;
                        _velocity.X = Math.Sign(_velocity.X) * Math.Min(_max_lateral_velocity, Math.Abs(_velocity.X));

                        // for target Y, we are gonna flap
                        // allowed to flap once?
                        _timer -= dt;
                        if(_timer < 0.0f)
                        {
                            // do I need to flap?
                            if(delta_y > FLAP_THRESHOLD_Y)
                            {
                                // determine flap impulse value
                                float flap_impulse = FLAP_IMPULSE_BASE;
                                flap_impulse *= _game.RandomFloat(0.80f, 1.20f);

                                // apply carrying/stress/health modifier

                                // flap once and rearm timer                                
                                _velocity.Y -= flap_impulse;

                                // clamp upwards velocity
                                _velocity.Y = Math.Max(-MAX_UPWARD_VELOCITY, _velocity.Y);

                                _timer = _game.RandomFloat(0.80f, 1.20f) * FLAP_DELAY_RELAX;
                            }
                        }

                        // and integrate position
                        _position += _velocity * dt;
                        break;

                    case FlyState.Stun:
                        {
                            // free fall during stun
                            _velocity += GRAVITY * dt;
                            _position += _velocity * dt;

                            _timer -= dt;
                            if(_timer <= 0.0f)
                            {
                                _fly_state = FlyState.Flying;
                                _timer = FLAP_DELAY_RELAX;
                            }
                        }
                        break;

                    case FlyState.Down:
                        {
                            // free fall to the ground :D
                            _velocity += GRAVITY * dt;
                            _position += _velocity * dt;
                        }
                        break;
                }

                // update hit box
                UpdateHitBox();
            }

            public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
            {
                spriteBatch.Draw(_texBirds, _hit_box, _rectOwlSpriteAwake, Color.White);
            }

            public void DrawDebugInfos(SpriteBatch spriteBatch, SpriteFont font)
            {
                string dbg_output = string.Format("Pos({0},{1})  Vel({2},{3})", (int)_position.X, (int)_position.Y, _velocity.X, _velocity.Y );
                spriteBatch.DrawString(font, dbg_output, new Vector2(10.0f, 30.0f), Color.Red);
            }

            public void SetTarget(int X, int Y)
            {
                _target_pos.X = (float)X;
                _target_pos.Y = (float)Y;
            }

            public bool OnSlap(int X, int Y)
            {
                if (!_hit_box.Contains(X, Y))
                    return false;

                if( _fly_state != FlyState.Down && _fly_state != FlyState.Stun )
                {
                    // what to do on a hit on a bird?
                    _hit_points--;
                    if(_hit_points == 0)
                    {
                        // the bird is "dead", freefall out of view!
                        _fly_state = FlyState.Down;

                        // add a little "kick" for the show :D
                        _velocity.Y += BIRD_DOWN_KICK;
                        _draw_in_background = true;
                    }
                    else
                    {
                        _timer = STUN_DURATION;
                        _fly_state = FlyState.Stun;
                        _velocity.Y += BIRD_STUN_KICK;

                        // => hit points, small free fall on slap *woot!*
                        // => change sprite, flap and speed values
                        // => on last hit, draw on background and freefall *WOOT!*

                        // play sound
                        // woot.
                        // woot!
                        // WOOT!

                        // response depends on type of bird
                        // drop nut, drop squirrel, drod shit
                    }

                    return true;
                }

                return false;
            }

            public Rectangle GetHitBox()
            {
                return _hit_box;
            }

            protected void UpdateHitBox()
            {
                _hit_box.X = (int)_position.X - WIDTH / 2;
                _hit_box.Y = (int)_position.Y - HEIGHT / 2;
                _hit_box.Width = WIDTH;
                _hit_box.Height = HEIGHT;
            }

            protected void SelectRandomTarget()
            {
                _target_pos.X = _game.RandomFloat(20.0f, Game1.SCREEN_WIDTH - 20.0f);
                _target_pos.Y = _game.RandomFloat(20.0f, Game1.SCREEN_HEIGHT - 20.0f);
            }

            protected void TakeOff()
            {
                _fly_state = FlyState.Flying;
                _velocity = Vector2.Zero;
                _timer = FLAP_DELAY_RELAX;
            }

            protected enum FlyState
            {
                Perched = 0,
                Flying,
                Stun,
                Down
            };

            protected Game1     _game;
            protected Rectangle _hit_box;
            protected Vector2   _position;
            protected Vector2   _velocity;
            protected FlyState  _fly_state;
            protected int       _hit_points;
            protected float     _timer;
            protected float     _max_lateral_velocity;
            protected bool      _draw_in_background;

            protected Vector2   _target_pos;
        }
    }
}
